#include    <stdio.h>
#include    <unistd.h>
#include    <sys/types.h>
#include    <sys/shm.h>
#include    <sys/sem.h>
#include    <sys/wait.h>
#include    <sys/ipc.h>
 
enum CONST
{
    BUFF_SIZE = 1024,
    PACKET_SIZE = 10000
};
 
typedef enum CONST CONST;
  
int
main(int argc, char *argv[])
{
    key_t shm_key, sem_key;
    int shm_id, sem_id;
    pid_t pid;
    char *buf;
    int i, n;
 
    struct sembuf up, down;
    union semun {
        int val;
        struct semid_ds *buf;  
        unsigned short *array;
        struct seminfo *__buf;  
    } set_op;
    //initiation of shared memory and semaphores
    shm_key = ftok("test", getpid());
    sem_key = ftok("test_sem", getpid());
    shm_id = shmget(shm_key, BUFF_SIZE , 0660 | IPC_CREAT);//shared memory
    sem_id = semget(sem_key, 3, 0660 | IPC_CREAT);//semaphores
 
    //initialisation of up and down operations
    up.sem_op = 1;
    down.sem_op = -1;
    //initialisation of semaphores
    set_op.val = 1;
    semctl(sem_id, 0, SETVAL, set_op);//0 semaphore is "mutex" semaphore
    set_op.val = BUFF_SIZE;
    semctl(sem_id, 1, SETVAL, set_op);//1 semaphore is "empty" semaphore
    set_op.val = 0;
    semctl(sem_id, 2, SETVAL, set_op);//2 semaphore is "full" semaphore
 
    if ((pid = fork()) < 0)
    {
        perror("error on creation process\n");
        return 1;
    }
    else if (pid == 0)
    {
        //reciever code  begins
        buf = shmat(shm_id, NULL, SHM_RDONLY);
 
        i = 0;
        do
        {
            down.sem_num = 2;
            semop(sem_id, &down, 1);//down(full)
            down.sem_num = 0;
            semop(sem_id, &down, 1);//down(mutex)
 
            i = (i + 1) % BUFF_SIZE;
            putchar(buf[i]);
 
            up.sem_num = 0;
            semop(sem_id, &up, 1);//up(mutex)
            up.sem_num = 1;
            semop(sem_id, &up, 1);//up(empty)
        }  while (buf[i] != '\0');
 
        shmdt(buf);
        return 0;
        //reciever code ends
    }
 
    //productioner code begins
    buf = shmat(shm_id, NULL, 0);
    n = 0;
    i = BUFF_SIZE - 1;
    while (n < PACKET_SIZE)
    {
            down.sem_num = 1;
            semop(sem_id, &down, 1);//down(empty)
            down.sem_num = 0;
            semop(sem_id, &down, 1);//down(mutex)
 
            i = (i + 1) % BUFF_SIZE;
            n++;
            if (n == PACKET_SIZE)
            {
                buf[i] = '\0';
            }
            else
            {
                buf[i] = 'A';
            }
 
            up.sem_num = 0;
            semop(sem_id, &up, 1);//up(mutex)
            up.sem_num = 2;
            semop(sem_id, &up, 1);//up(full)
    }
    //productioner code ends
    wait(&i);
    shmdt(buf);
    shmctl(shm_id, IPC_RMID, NULL);
    semctl(sem_id, 0, IPC_RMID);
    return 0;
}